/**
 * File:   RoutingTable.java
 * Author: Gvennet
 */
package dht.async;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** todo write javadoc */
public class RoutingTable
{

    private static final int MAX_BUCKET_SIZE = 20;

    private final Identifier ownId;

    private final List<Node>[] rt;

    private final Lock[] lockList;  //todo!!!

    protected RoutingTable(Identifier id)
    {
        ownId = id;
        int length = id.getLength() * 8;
        rt = new List[length];
        lockList = new Lock[length];
        for (int i = 0; i < length; i++)
        {
            lockList[i] = new ReentrantLock();
            rt[i] = new ArrayList<Node>();
        }
    }

    protected void lockBucket(int i)
    {
        lockList[i].lock();
    }

    protected void unlockBucket(int i)
    {
        lockList[i].unlock();
    }

    protected boolean havePlace(Identifier id)
    {
        boolean result;
        int i = findBucket(id);
        lockBucket(i);
        try
        {
            result = rt[i].size() < MAX_BUCKET_SIZE;
        }
        finally
        {
            unlockBucket(i);
        }
        return result;
    }

    protected boolean contains(Identifier id)
    {
//        int i = index(id);
//        locks[i].lock();
//        try {
//
//        } finally {
//           locks[i].unlock();
//        }
        boolean result;
        int i = findBucket(id);
        lockBucket(i);
        try
        {
            List<Node> nodes = rt[i];
            result = false;
            for (Node node : nodes)
            {
                Identifier ident = node.getIdentifier();
                if (ident.equals(id))
                {
                    result = true;
                    break;
                }
            }
        }
        finally
        {
            unlockBucket(i);
        }
        return result;
    }

    protected void makeFirst(Identifier id)
    {
        int i = findBucket(id);
        Node processingNode = null;
        lockBucket(i);
        try
        {
            List<Node> list = rt[i];
            for (Node node : list)
            {
                Identifier identifier = node.getIdentifier();
                if (identifier.equals(id))
                {
                    processingNode = node;
                }
            }
            if (processingNode != null)
            {
            list.remove(processingNode);
            list.add(0, processingNode);
            }
        }
        finally
        {
            unlockBucket(i);
        }
    }

    protected void remove(Node node)
    {
        int i = findBucket(node.getIdentifier());
        lockBucket(i);
        try
        {
            rt[i].remove(node);
        }
        finally
        {
            unlockBucket(i);
        }
    }

    protected void add(Node node)
    {
        int i = findBucket(node.getIdentifier());
        lockBucket(i);
        try
        {
            rt[i].add(0, node);
        }
        finally
        {
            unlockBucket(i);
        }
    }

    protected List<Node> getBucket(Identifier id)
    {
        List<Node> result;
        int i = findBucket(id);
        lockBucket(i);
        try
        {
            result = new ArrayList<Node>(rt[i]);
        }
        finally
        {
            unlockBucket(i);
        }
        return result;
    }

    protected List<Node> getBucket(int number)
    {
        return rt[number];
    }


    protected List<Node> getAllNodes()
    {
        List<Node> list = new ArrayList<Node>();

        for (List<Node> aRt : rt)
        {
            list.addAll(aRt);
        }

        return list;
    }

    int findBucket(Identifier id)    //todo
    {
        int i;

        byte[] distance = ((ByteArrayIdentifier) ownId.getDistance(id)).getByteValue();

        for (i = 0; i < 19; i++)
        {
            if (distance[i] != 0)
            {
                break;
            }
        }
        if (i == 19)
        {
            return 0;
        }
        for (int j = 0; j < 7; j++)
        {
            if ((distance[i] & (1 << j)) > 0)
            {
                return 159 - (i * 8 + j);
            }
        }
        return 159 - i * 8;
    }
}
